perf(android): Parse app start profiling config without JsonSerializer - #5867
Open
runningcode wants to merge 4 commits into
Open
perf(android): Parse app start profiling config without JsonSerializer#5867runningcode wants to merge 4 commits into
runningcode wants to merge 4 commits into
Conversation
📲 Install BuildsAndroid
|
Contributor
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 6b019b7 | 343.31 ms | 417.23 ms | 73.91 ms |
| d15471f | 342.08 ms | 415.44 ms | 73.35 ms |
| 8687935 | 332.52 ms | 362.23 ms | 29.71 ms |
| 5b1a06b | 352.27 ms | 413.70 ms | 61.43 ms |
| 91bb874 | 314.47 ms | 440.00 ms | 125.53 ms |
| 0ee65e9 | 321.06 ms | 361.24 ms | 40.18 ms |
| e63ad34 | 323.67 ms | 390.33 ms | 66.67 ms |
| 33a08cc | 267.08 ms | 340.45 ms | 73.37 ms |
| 27d7cf8 | 397.90 ms | 498.65 ms | 100.75 ms |
| ee747ae | 405.43 ms | 485.70 ms | 80.28 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 6b019b7 | 0 B | 0 B | 0 B |
| d15471f | 1.58 MiB | 2.13 MiB | 559.54 KiB |
| 8687935 | 1.58 MiB | 2.19 MiB | 619.17 KiB |
| 5b1a06b | 0 B | 0 B | 0 B |
| 91bb874 | 1.58 MiB | 2.13 MiB | 559.07 KiB |
| 0ee65e9 | 0 B | 0 B | 0 B |
| e63ad34 | 0 B | 0 B | 0 B |
| 33a08cc | 1.58 MiB | 2.12 MiB | 555.28 KiB |
| 27d7cf8 | 1.58 MiB | 2.12 MiB | 549.42 KiB |
| ee747ae | 1.58 MiB | 2.10 MiB | 530.95 KiB |
8 tasks
…r (JAVA-621) SentryPerformanceProvider.launchAppStartProfiler ran in ContentProvider.onCreate — main thread, before Application.onCreate, on every cold start — and built a full JsonSerializer(SentryOptions.empty()) to read one small config file. That path only ever reads options.getLogger(), but JsonSerializer's constructor registers every known deserializer to use exactly one of them. Calling the deserializer directly cuts the parse from 221 to 33 allocations and ~7.5us to ~3.7us (Pixel 10, androidx Microbenchmark, allocationCount is deterministic). Malformed input still yields null so callers keep reporting it as a deserialization failure rather than a read error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…onfig Narrow the deserialization catch from Throwable to Exception. The vendored JSON reader signals bad input with IOException (MalformedJsonException, EOFException), IllegalStateException on token type mismatch, and NumberFormatException on an unparseable number — all Exception subclasses. Catching Throwable additionally swallowed Error, which is never a recoverable "config file is bad" signal. This also restores parity with JsonSerializer.deserialize, which catches Exception, so the replaced behaviour is matched exactly rather than widened. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
runningcode
force-pushed
the
no/java-621-app-start-profiling-config-deserialization
branch
from
July 30, 2026 09:22
162d4db to
8e27d0c
Compare
runningcode
marked this pull request as ready for review
July 30, 2026 09:27
runningcode
requested review from
0xadam-brown,
adinauer,
markushi and
romtsn
as code owners
July 30, 2026 09:27
0xadam-brown
approved these changes
Jul 30, 2026
| * them. | ||
| * | ||
| * <p>Returns null on malformed input, matching what {@code JsonSerializer.deserialize} did, so | ||
| * callers keep reporting it as a deserialization failure rather than a read error. The vendored |
Member
There was a problem hiding this comment.
nit if you want it: Personally I'd either inline "The vendored..." above the catch clause, or drop it altogether. (The latter makes sense to me as we're all aware of the virtues of not catching Errors.)
Drop the paragraph enumerating which exception types the vendored JSON reader throws. That catch (Exception) does not swallow Error is a language-level given, and listing the reader's internal exception types invites the comment to drift as that code changes. The rationale a reader cannot infer from the code — why the deserializer is called directly, and why null is returned instead of rethrowing — stays. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📜 Description
JsonSerializeris a massive object with 60 serializers but we only need one of them inSentryPerformanceProvider. We can't re-use the one held inSentryOptionsbecause theSentryPerformanceProvidercontent provider has a higher priority than theSentryInitProvider.So instead we create a serializer with only the serializer we need for this specific purpose.
Measured on a Pixel 10 (API 37) with androidx Microbenchmark, release build:
JsonSerializer(before)Deserializer(after)allocationCountis deterministic, so the allocation figures are exact. The timings are steady-state (JIT-warmed, clocks unlocked), so they understate the real cold-start saving — warmup amortizes away the class loading and verification of all those deserializer classes, which the real one-shot path pays in full. No claim is made about measurable end-to-end startup improvement; ~3.9 µs is small against a ~1 s cold start. The point is main-thread work removed from beforeApplication.onCreate.💡 Motivation and Context
Perf improvement
Related: #5708 and #5709 were previously closed for the same "small allocation win, not worth the trade off" reason.
GH Issue: #5707
Linear: #5707
💚 How did you test it?
Unit tests
📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps
createAndStartContinuousProfileralso callsSentryOptions.empty(), forTracesSampler. That one genuinely needs a mutable options object (setProfileSessionSampleRate) and only runs when a sampled continuous-profiling config exists, so it is deliberately left alone.